package com.cv.system.service.user;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.cv.framework.common.enums.CommonStatusEnum;
import com.cv.framework.common.pojo.PageResult;
import com.cv.framework.security.core.utils.SecurityFrameworkUtil;
import com.cv.infra.api.file.FileApi;
import com.cv.platform.api.comment.CvCommentApi;
import com.cv.platform.api.commentReply.CvCommentReplyApi;
import com.cv.platform.api.message.CvMessageApi;
import com.cv.platform.api.report.CvReportApi;
import com.cv.platform.api.subscribe.SubscribeApi;
import com.cv.platform.api.video.CvVideoApi;
import com.cv.system.controller.audit.auth.vo.AuditUserLoginProfileRespVO;
import com.cv.system.controller.platform.auth.vo.PlatformUserLoginProfileRespVO;
import com.cv.system.controller.platform.user.vo.PlatformUserPasswordReqVO;
import com.cv.system.controller.platform.user.vo.PlatformUserProfilePasswordReqVO;
import com.cv.system.controller.platform.user.vo.PlatformUserProfileRespVO;
import com.cv.system.controller.platform.user.vo.PlatformUserProfileSaveReqVO;
import com.cv.system.controller.system.auth.vo.UserLoginProfileRespVO;
import com.cv.system.controller.system.user.vo.profile.UserProfilePasswordReqVO;
import com.cv.system.controller.system.user.vo.profile.UserProfileRespVO;
import com.cv.system.controller.system.user.vo.profile.UserProfileSaveReqVO;
import com.cv.system.controller.system.user.vo.user.UserPageReqVO;
import com.cv.system.controller.system.user.vo.user.UserRespVO;
import com.cv.system.controller.system.user.vo.user.UserSaveReqVO;
import com.cv.system.dal.dataobject.user.SystemUsersDO;
import com.cv.system.dal.mapper.user.SystemUserMapper;
import com.cv.system.service.role.SystemRoleService;
import com.google.common.annotations.VisibleForTesting;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.cv.framework.common.exception.utils.ServiceExceptionUtil.exception;
import static com.cv.system.ErrorCodeConstants.VALIDATE_OLD_PASSWORD_NOT_MATCH;
import static com.cv.system.ErrorCodeConstants.VALIDATE_USER_NOT_FOUND;

/**
 * 系统用户业务层接口实现类
 *
 * @author Charles_XDXD
 */
@Service
@Validated
public class SystemUserServiceImpl implements SystemUserService {

    @Resource
    private SystemUserMapper systemUserMapper;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private UserInfoValidateService userInfoValidateService;

    @Resource
    private SystemRoleService systemRoleService;

    @DubboReference
    private FileApi fileApi;

    @DubboReference
    private SubscribeApi subscribeApi;

    @DubboReference
    private CvMessageApi cvMessageApi;

    @DubboReference
    private CvVideoApi cvVideoApi;

    @DubboReference
    private CvCommentApi cvCommentApi;

    @DubboReference
    private CvCommentReplyApi cvCommentReplyApi;

    @DubboReference
    private CvReportApi cvReportApi;

    @Override
    public SystemUsersDO getUserByUsername(String username) {
        return systemUserMapper.selectByUsername(username);
    }

    @Override
    public SystemUsersDO getUserByEmail(String email) {
        return systemUserMapper.selectByEmail(email);
    }

    @Override
    public SystemUsersDO getUserByMobile(String mobile) {
        return systemUserMapper.selectByMobile(mobile);
    }

    @Override
    public PageResult<UserRespVO> getUserPage(UserPageReqVO pageReqVO) {
        return systemUserMapper.selectUserRespPage(pageReqVO);
    }

    @Override
    public UserRespVO getUserDetail(Long userId) {
        SystemUsersDO systemUsersDO = systemUserMapper.selectById(userId);
        UserRespVO respVO = BeanUtil.toBean(systemUsersDO, UserRespVO.class);

        if (ObjUtil.isNotEmpty(respVO.getAvatar())) {
            respVO.setAvatarUrl(fileApi.getUrl(respVO.getAvatar()));
        }

        return respVO;
    }

    @Override
    public UserProfileRespVO getUserProfile(Long userId) {
        SystemUsersDO systemUsersDO = systemUserMapper.selectById(userId);
        UserProfileRespVO respVO = BeanUtil.toBean(systemUsersDO, UserProfileRespVO.class);

        if (ObjUtil.isNotEmpty(systemUsersDO.getAvatar())) {
            respVO.setAvatar(fileApi.getUrl(systemUsersDO.getAvatar()));
        }

        return respVO;
    }

    @Override
    public PlatformUserProfileRespVO getUserPlatformProfile(Long userId) {
        SystemUsersDO systemUsersDO = systemUserMapper.selectById(userId);
        PlatformUserProfileRespVO respVO = BeanUtil.toBean(systemUsersDO, PlatformUserProfileRespVO.class);

        if (ObjUtil.isEmpty(systemUsersDO)) {
            return new PlatformUserProfileRespVO();
        }

        if (ObjUtil.isNotEmpty(systemUsersDO.getAvatar())) {
            respVO.setAvatar(fileApi.getUrl(systemUsersDO.getAvatar()));
        }
        respVO.setFansCount(subscribeApi.getFansCount(userId));
        respVO.setFollowCount(subscribeApi.getFollowCount(userId));

        return respVO;
    }

    @Override
    public AuditUserLoginProfileRespVO getUserAuditProfile(Long userId) {
        if (ObjUtil.isEmpty(userId)) {
            return null;
        }

        SystemUsersDO systemUsersDO = systemUserMapper.selectById(userId);
        if (ObjUtil.isNotEmpty(systemUsersDO)) {
            AuditUserLoginProfileRespVO respVO = BeanUtil.toBean(systemUsersDO, AuditUserLoginProfileRespVO.class);
            respVO.setAvatar(fileApi.getUrl(systemUsersDO.getAvatar()));
            // 这里设置为 1 个，未来如果有需要再考虑添加多个权限
            respVO.setRoles(new Long[]{systemRoleService.getUserRole(userId)});
            return respVO;
        }

        return null;
    }

    @Override
    public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createUser(UserSaveReqVO userSaveReqVO) {
        userInfoValidateService.validateUsernameUnique(null, userSaveReqVO.getUsername());
        userInfoValidateService.validateUserMobileUnique(null, userSaveReqVO.getMobile());
        userInfoValidateService.validateUserEmailUnique(null, userSaveReqVO.getEmail());

        SystemUsersDO systemUsersDO = BeanUtil.toBean(userSaveReqVO, SystemUsersDO.class);

        if (StrUtil.isEmpty(systemUsersDO.getNickname())) {
            systemUsersDO.setNickname("用户" + systemUsersDO.getUsername());
        }

        systemUsersDO.setStatus(CommonStatusEnum.ENABLE.getStatus());
        systemUsersDO.setPassword(passwordEncoder.encode(systemUsersDO.getPassword()));
        systemUserMapper.insert(systemUsersDO);
        return systemUsersDO.getId();
    }

    @Override
    public void updateUser(UserSaveReqVO userSaveReqVO) {
        validateUserExists(userSaveReqVO.getId());

        /*
         * 后台系统无权修改用户密码
         * 因此，将密码设为空值。MybatisPlus 会自动忽略空值。
         */
        userSaveReqVO.setPassword(null);

        userInfoValidateService.validateUsernameUnique(userSaveReqVO.getId(), userSaveReqVO.getUsername());
        userInfoValidateService.validateUserMobileUnique(userSaveReqVO.getId(), userSaveReqVO.getMobile());
        userInfoValidateService.validateUserEmailUnique(userSaveReqVO.getId(), userSaveReqVO.getEmail());

        SystemUsersDO systemUsersDO = BeanUtil.toBean(userSaveReqVO, SystemUsersDO.class);

        if (StrUtil.isEmpty(systemUsersDO.getNickname())) {
            systemUsersDO.setNickname("用户" + systemUsersDO.getUsername());
        }

        systemUserMapper.updateById(systemUsersDO);
    }

    @Override
    public void updateUserStatus(Long userId, Integer status) {
        validateUserExists(userId);

        systemUserMapper.update(new LambdaUpdateWrapper<SystemUsersDO>()
                .set(SystemUsersDO::getStatus, status)
                .eq(SystemUsersDO::getId, userId));
    }

    @Override
    public void updateUserProfile(Long userId, UserProfileSaveReqVO userProfileSaveReqVO) {
        validateUserExists(userId);
        userInfoValidateService.validateUserMobileUnique(userId, userProfileSaveReqVO.getMobile());
        userInfoValidateService.validateUserEmailUnique(userId, userProfileSaveReqVO.getEmail());

        SystemUsersDO systemUsersDO = BeanUtil.toBean(userProfileSaveReqVO, SystemUsersDO.class);
        systemUserMapper.updateById(systemUsersDO);
    }

    @Override
    public void updateUserProfile(PlatformUserProfileSaveReqVO platformUserProfileSaveReqVO) {
        SystemUsersDO systemUsersDO = BeanUtil.toBean(platformUserProfileSaveReqVO, SystemUsersDO.class);
        systemUsersDO.setId(SecurityFrameworkUtil.getLoginUserId());
        systemUserMapper.updateById(systemUsersDO);
    }

    @Override
    public void updateAvatar(Long userId, Long avatar) {
        systemUserMapper.update(new LambdaUpdateWrapper<SystemUsersDO>()
                .set(SystemUsersDO::getAvatar, avatar)
                .eq(SystemUsersDO::getId, userId));
    }

    @Override
    public void updateUserProfilePassword(Long userId, UserProfilePasswordReqVO userProfilePasswordReqVO) {
        validateUserExists(userId);

        validateOldPasswordMatch(userId, userProfilePasswordReqVO.getOldPassword());

        systemUserMapper.update(new LambdaUpdateWrapper<SystemUsersDO>()
                .set(SystemUsersDO::getPassword, passwordEncoder.encode(userProfilePasswordReqVO.getNewPassword()))
                .eq(SystemUsersDO::getId, userId));
    }

    @Override
    public void modifyUserPassword(PlatformUserProfilePasswordReqVO reqVO) {
        validateOldPasswordMatch(SecurityFrameworkUtil.getLoginUserId(), reqVO.getOldPassword());

        systemUserMapper.update(new LambdaUpdateWrapper<SystemUsersDO>()
                .set(SystemUsersDO::getPassword, passwordEncoder.encode(reqVO.getNewPassword()))
                .eq(SystemUsersDO::getId, SecurityFrameworkUtil.getLoginUserId()));
    }

    @Override
    public void modifyUserPassword(PlatformUserPasswordReqVO reqVO) {
        systemUserMapper.update(new LambdaUpdateWrapper<SystemUsersDO>()
                .set(SystemUsersDO::getPassword, passwordEncoder.encode(reqVO.getNewPassword()))
                .eq(SystemUsersDO::getUsername, reqVO.getUsername()));
    }

    @Override
    public void modifyEmail(String email) {
        if (StrUtil.isNotEmpty(email)) {
            Long userId = SecurityFrameworkUtil.getLoginUserId();
            userInfoValidateService.validateUserEmailUnique(userId, email);

            systemUserMapper.update(new LambdaUpdateWrapper<SystemUsersDO>()
                    .set(SystemUsersDO::getEmail, email)
                    .eq(SystemUsersDO::getId, userId));
        }
    }

    @Override
    public void deleteUser(Long userId) {
        validateUserExists(userId);
        systemUserMapper.deleteById(userId);
        cvVideoApi.deleteAllVideo(userId);
        cvMessageApi.deleteAllMessage(userId);
        cvCommentApi.deleteAllComment(userId);
        cvCommentReplyApi.deleteAllCommentReply(userId);
        subscribeApi.deleteAllSubscribe(userId);
        cvReportApi.deleteAllReport(userId);
    }

    @Override
    public void updateLoginIpAndTime(Long userId, String ip) {
        LambdaUpdateWrapper<SystemUsersDO> wrapper = new LambdaUpdateWrapper<SystemUsersDO>()
                .set(SystemUsersDO::getLoginIp, ip)
                .set(SystemUsersDO::getLoginDate, LocalDateTime.now())
                .eq(SystemUsersDO::getId, userId);

        systemUserMapper.update(wrapper);
    }

    @Override
    public List<Long> queryUserIdsByNickname(String nickname) {
        if (StrUtil.isNotEmpty(nickname)) {
            return systemUserMapper.selectList(new LambdaQueryWrapper<SystemUsersDO>()
                            .like(SystemUsersDO::getNickname, nickname))
                    .stream().map(SystemUsersDO::getId).collect(Collectors.toList());
        }

        return Collections.emptyList();
    }

    @Override
    public String queryNicknameByUserId(Long userId) {
        if (ObjUtil.isEmpty(userId)) {
            return StrUtil.EMPTY;
        }

        return Optional.ofNullable(systemUserMapper.selectById(userId))
                .orElse(new SystemUsersDO().setNickname(StrUtil.EMPTY))
                .getNickname();
    }

    @Override
    public UserLoginProfileRespVO getLoginUserProfile(Long userId) {
        if (ObjUtil.isEmpty(userId)) {
            return null;
        }

        SystemUsersDO systemUsersDO = systemUserMapper.selectById(userId);
        if (ObjUtil.isNotEmpty(systemUsersDO)) {
            UserLoginProfileRespVO respVO = BeanUtil.toBean(systemUsersDO, UserLoginProfileRespVO.class);
            respVO.setAvatar(fileApi.getUrl(systemUsersDO.getAvatar()));
            // 这里设置为 1 个，未来如果有需要再考虑添加多个权限
            respVO.setRoles(new Long[]{systemRoleService.getUserRole(userId)});
            return respVO;
        }

        return null;
    }

    @Override
    public PlatformUserLoginProfileRespVO getLoginUserPlatformProfile(Long userId) {
        if (ObjUtil.isEmpty(userId)) {
            return null;
        }

        SystemUsersDO systemUsersDO = systemUserMapper.selectById(userId);
        if (ObjUtil.isNotEmpty(systemUsersDO)) {
            PlatformUserLoginProfileRespVO respVO = BeanUtil.toBean(systemUsersDO, PlatformUserLoginProfileRespVO.class);
            respVO.setAvatar(fileApi.getUrl(systemUsersDO.getAvatar()));
            // 这里设置为 1 个，未来如果有需要再考虑添加多个权限
            respVO.setRoles(new Long[]{systemRoleService.getUserRole(userId)});
            return respVO;
        }

        return null;
    }

    @VisibleForTesting
    void validateOldPasswordMatch(Long userId, String oldPassword) {
        if (!isPasswordMatch(oldPassword, systemUserMapper.selectById(userId).getPassword())) {
            throw exception(VALIDATE_OLD_PASSWORD_NOT_MATCH);
        }
    }

    @VisibleForTesting
    void validateUserExists(Long userId) {
        if (ObjUtil.isEmpty(userId)) {
            throw exception(VALIDATE_USER_NOT_FOUND);
        }

        SystemUsersDO systemUsersDO = systemUserMapper.selectById(userId);
        if (ObjUtil.isEmpty(systemUsersDO)) {
            throw exception(VALIDATE_USER_NOT_FOUND);
        }
    }

}
